package com.weilele.mvvm.utils.fragment

import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.annotation.NonNull
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.lifecycleScope
import com.weilele.mvvm.MvvmConf
import com.weilele.mvvm.R
import com.weilele.mvvm.base.MvvmActivity
import com.weilele.mvvm.base.helper.appCompatActivity
import com.weilele.mvvm.base.helper.ignoreError
import com.weilele.mvvm.utils.`object`.ScreenAdaptationObj
import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.CoroutineContext


/**
 * 添加fragment
 */
fun AppCompatActivity?.addFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    tag: String? = null,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.addFragment(containerViewId, fragment, tag, beforeCommit)
}

/**
 * 添加fragment
 */
fun Fragment?.addFragment(
    @IdRes containerViewId: Int,
    @NonNull fragment: Fragment,
    tag: String? = null,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.addFragment(containerViewId, fragment, tag, beforeCommit)
}

/**
 * 添加fragment
 */
fun FragmentManager?.addFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment, tag: String? = null,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .add(containerViewId, fragment, tag)
            .commitAllowingStateLoss()
    }
}

/**
 * 添加fragment
 */
fun AppCompatActivity?.addFragment(
    tag: String,
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.addFragment(tag, fragment, beforeCommit)
}

/**
 * 添加fragment
 */
fun Fragment?.addFragment(
    tag: String,
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.addFragment(tag, fragment, beforeCommit)
}

/**
 * 添加fragment
 * 不会被当作view 显示在界面上
 */
fun FragmentManager?.addFragment(
    tag: String,
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .add(fragment, tag)
            .commitAllowingStateLoss()
    }
}

/**
 * 移除fragment
 */
fun Fragment?.removeFragment(
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.removeFragment(fragment, beforeCommit)
}

fun AppCompatActivity?.removeFragment(
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.removeFragment(fragment, beforeCommit)
}

/**
 * 移除fragment
 */
fun FragmentManager?.removeFragment(
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .remove(fragment)
            .commitAllowingStateLoss()
    }
}

/**
 * replace fragment
 */
fun Fragment?.replaceFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.replaceFragment(containerViewId, fragment, beforeCommit)
}

/**
 * replace fragment
 */
fun AppCompatActivity?.replaceFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.replaceFragment(containerViewId, fragment, beforeCommit)
}

/**
 * replace fragment
 */
fun FragmentManager?.replaceFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .replace(containerViewId, fragment)
            .commitAllowingStateLoss()
    }
}

fun Fragment.setScreenAdaptation() {
    val act = appCompatActivity
    if (MvvmConf.IS_CAN_SCREEN_ADAPTATION) {
        if (act is MvvmActivity) {
            if (act.isNeedScreenAdaptation()) {
                ScreenAdaptationObj.setCustomDensity(resources)
            }
        } else {
            if (MvvmConf.IS_CAN_SCREEN_OTHER_LIBRARY_ADAPTATION) {
                ScreenAdaptationObj.setCustomDensity(resources)
            }
        }
    }
}

/**
 * 设置携带数据
 */
fun <T : Fragment> T.arguments(bundle: Bundle? = null, setBundleValue: (Bundle.() -> Unit)?): T {
    val arg = bundle ?: Bundle()
    setBundleValue?.invoke(arg)
    arguments = arg
    return this
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> Fragment?.navigateTo(
    containerViewId: Int, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    return this?.childFragmentManager.navigateTo(
        containerViewId,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> AppCompatActivity?.navigateTo(
    containerView: View, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    if (containerView.id == View.NO_ID) {
        containerView.id = View.generateViewId()
    }
    return this?.supportFragmentManager.navigateTo(
        containerView.id,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> Fragment?.navigateTo(
    containerView: View, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    if (containerView.id == View.NO_ID) {
        containerView.id = View.generateViewId()
    }
    return this?.childFragmentManager.navigateTo(
        containerView.id,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> AppCompatActivity?.navigateTo(
    containerViewId: Int, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    return this?.supportFragmentManager.navigateTo(
        containerViewId,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> FragmentManager?.navigateTo(
    containerViewId: Int,
    fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    this ?: return null
    fg ?: return null
    val lastFg = ignoreError {
        if (backStackEntryCount > 0) {
            findFragmentByTag(getBackStackEntryAt(backStackEntryCount - 1).name)
        } else {
            null
        }
    }
    val tag = fg.hashCode().toString()
    addFragment(containerViewId, fg, tag) {
        setCustomAnimations(
            R.anim.activity_open_enter, R.anim.activity_open_exit,
            R.anim.activity_close_enter, R.anim.activity_close_exit
        )
        addToBackStack(tag)
        if (hideTopStackFragment) {
            lastFg?.let {
                hide(it)
            }
        }
        beforeCommit?.invoke(this, lastFg)
    }
    return fg
}

/**
 *  当fragment的生命周期是onCreate时回到
 *  仅回调一次
 */
fun <T : Fragment> T.whenFirstCreated(block: CoroutineScope.(fragment: T) -> Unit) {
    this.lifecycleScope.launchWhenCreated {
        block.invoke(this, this@whenFirstCreated)
    }
}

/**
 *  当fragment的生命周期是onCreate时回到
 *  仅回调一次
 */
fun <T : Fragment> T.whenFirstStarted(block: CoroutineScope.(fragment: T) -> Unit) {
    this.lifecycleScope.launchWhenStarted {
        block.invoke(this, this@whenFirstStarted)
    }
}

/**
 *  当fragment的生命周期是onCreate时回到
 *  仅回调一次
 */
fun <T : Fragment> T.whenFirstResumed(block: CoroutineScope.(fragment: T) -> Unit) {
    this.lifecycleScope.launchWhenResumed {
        block.invoke(this, this@whenFirstResumed)
    }
}